import { _decorator, Component, Node, UITransform, Graphics, Color, instantiate, isValid } from 'cc';
import { createUINode } from '../Utils';
import QuadTree from '../Common/QuadTree';
import { ApiMsgEnum, EntityTypeEnum, GMoveSpeed, G, IClientInput, IMsgClientSync, IMsgServerSync, IMsgStateSync, IState, deepClone } from '../Common';
import { ActorManager } from '../Entity/Actor/ActorManager';
import { EnemyManager } from '../Entity/Enemy/EnemyManager';
import { WeaponManager } from '../Entity/Weapon/WeaponManager';
import { BulletManager } from '../Entity/Bullet/BulletManager';
import { ObjectPoolManager } from '../Runtime/ObjectPoolManager';
import DataManager from '../Runtime/DataManager';
import { EventManager } from '../Runtime/EventManager';
import { EventTypeEnum, UIEnum } from '../Enum';
import { NetworkManager } from '../Runtime/NetworkManager';
import { GameLogic } from '../Common/Runtime/GameLogic';
import { Simulator } from '../Common/RVO/Simulator';
import { Vector2 } from '../Common/RVO/Common';
import Boss1Tree from '../Common/BehaviorTree/Biz/Tree/Boss1Tree';
import { BehaviorManager } from '../Common/BehaviorTree/BehaviorManager';
import { showToast } from '../Runtime/MainManager';
import { ExpManager } from '../Entity/Exp/ExpManager';
import ResourcesManager from '../Runtime/ResourcesManager';
import { UIManager } from '../Runtime/UIManager';
import Boss2Tree from '../Common/BehaviorTree/Biz/Tree/Boss2Tree';
const { ccclass } = _decorator;

@ccclass('BattleScene')
export class BattleScene extends Component {

    expStage: Node = null
    bulletStage: Node = null
    labelStage: Node = null
    Camera: Node

    stage: Node = null
    graphics: Graphics = null
    pendingMsg: IMsgClientSync[] = []
    curRenderState: IState
    async init() {
        this.Camera = this.node.getChildByName('Camera')
        UIManager.Ins.show(UIEnum.BattleUI, {}, this.Camera)

        DataManager.Ins.isSingle = false
        DataManager.Ins.bm = this
        DataManager.Ins.simulator = new Simulator()
        // 构造四叉树结构
        DataManager.Ins.quadTree = new QuadTree({
            x: -G.StageSize.w / 2,
            y: -G.StageSize.h / 2,
            w: G.StageSize.w,
            h: G.StageSize.h,
        }, 1)

        GameLogic.Ins.initAgent(DataManager.Ins.simulator)
        GameLogic.Ins.setLevelData(DataManager.Ins.state)

        this.generateStage()

        EventManager.Ins.on(EventTypeEnum.clientInput, this.handleClientInput, this)
        NetworkManager.Ins.listening(ApiMsgEnum.MsgServerSync, this.handleMsgServerSync, this)
        NetworkManager.Ins.listening(ApiMsgEnum.MsgStateSync, this.handleMsgStateSync, this)
    }

    update(dt: number) {

        this.render(dt)
        this.actorTick(dt)

        DataManager.Ins.renderTotalTime += dt
    }

    actorTick(dt: number) {
        for (const actor of DataManager.Ins.state.actors) {
            const { id } = actor
            const am = DataManager.Ins.actorMap.get(id)
            am.tick(dt)
        }
    }

    render(dt: number) {

        this.renderActor(dt)

        // 渲染 （未渲染的状态 到 当前渲染帧时间） 状态
        for (const state of DataManager.Ins.stateMap) {
            if (DataManager.Ins.renderTotalTime > state[0]) {
                this.curRenderState = state[1]
                if (!this.curRenderState) {
                    break
                }
                this.renderEnemy(dt)
                this.renderBullet(dt)
                this.renderWeapon(dt)
                this.renderExpBall()
            } else {
                break
            }
        }

        // 移除当前渲染帧之前的状态
        for (const state of DataManager.Ins.stateMap) {
            if (DataManager.Ins.renderTotalTime > state[0]) {
                DataManager.Ins.stateMap.delete(state[0])
            } else {
                break
            }
        }

        this.setEnemySibling()
    }

    renderActor(dt: number) {
        for (const actor of DataManager.Ins.state.actors) {
            const { id, type } = actor
            let am = DataManager.Ins.actorMap.get(id)
            if (!am) {
                const node = createUINode(type)
                am = node.addComponent(ActorManager)
                am.init(actor)
                node.parent = this.stage
                DataManager.Ins.actorMap.set(id, am)
            } else {
                am.render(actor)
            }
        }
    }

    renderEnemy(dt: number) {
        for (const enemy of this.curRenderState.enemies) {
            const { id, type } = enemy
            let em = DataManager.Ins.enemyMap.get(id)
            if (!em) {
                const node = ObjectPoolManager.Ins.poolAlloc(type)
                em = node.getComponent(EnemyManager)
                if (!em) {
                    node.parent = this.stage
                    em = node.addComponent(EnemyManager)
                    em.init(enemy)
                }
                DataManager.Ins.enemyMap.set(id, em)
                em.reset(enemy)
            }
            em.render(enemy)
        }
    }

    renderWeapon(dt: number) {
        for (const weapon of this.curRenderState.weapons) {
            const { id, entityType, owner } = weapon
            let wm = DataManager.Ins.weaponMap.get(id)
            if (!wm) {
                const actor = DataManager.Ins.actorMap.get(owner)
                if (actor) {
                    const parent = actor.node
                    if (!isValid(parent, true)) {
                        return
                    }
                    const prefab = ResourcesManager.Ins.prefabMap.get(entityType)
                    if (!prefab) {
                        return
                    }
                    const node = instantiate(prefab)
                    node.parent = parent
                    wm = node.addComponent(WeaponManager)
                    DataManager.Ins.weaponMap.set(id, wm)
                    wm.init(weapon)
                }
            }
            wm.render(weapon, dt)
        }
    }

    renderBullet(dt: number) {
        for (const bullet of this.curRenderState.bullets) {
            const { id, type } = bullet
            let bm = DataManager.Ins.bulletMap.get(id)
            if (!bm) {
                const node = ObjectPoolManager.Ins.poolAlloc(type)
                bm = node.getComponent(BulletManager)
                if (!bm) {
                    node.parent = this.bulletStage
                    bm = node.addComponent(BulletManager)
                    bm.init(bullet)
                }
                DataManager.Ins.bulletMap.set(id, bm)
                bm.reset(bullet)
            }
            bm.render(bullet, dt)
        }
        for (const bullet of this.curRenderState.enemyBullets) {
            const { id, type } = bullet
            let bm = DataManager.Ins.enemyBulletMap.get(id)
            if (!bm) {
                const node = ObjectPoolManager.Ins.poolAlloc(type)
                bm = node.getComponent(BulletManager)
                if (!bm) {
                    node.parent = this.bulletStage
                    bm = node.addComponent(BulletManager)
                    bm.init(bullet)
                }
                DataManager.Ins.enemyBulletMap.set(id, bm)
                bm.reset(bullet)
            }
            bm.render(bullet, dt)
        }
    }

    renderExpBall() {
        for (const exp of this.curRenderState.exps) {
            const { type, pos, id, state } = exp
            let em = DataManager.Ins.expBallMap.get(id)
            if (!em) {
                const node = ObjectPoolManager.Ins.poolAlloc(EntityTypeEnum.exp)
                node.parent = this.expStage
                em = node.getComponent(ExpManager)
                DataManager.Ins.expBallMap.set(id, em)
                em.init(type, id)
            }
            em.render(pos, state)
        }
    }

    graphicsLine() {

        let fun = (quadTree: QuadTree) => {
            this.graphics.rect(
                quadTree.bounds.x,
                quadTree.bounds.y,
                quadTree.bounds.w,
                quadTree.bounds.h,
            )
            quadTree.nodes.forEach(v => {
                fun(v)
            });
        }

        this.graphics.clear()
        fun(DataManager.Ins.quadTree)
        this.graphics.strokeColor = Color.RED
        this.graphics.lineWidth = 5
        this.graphics.stroke()

    }

    /** 修改层级 */
    setEnemySibling() {
        if (!this.stage) {
            return
        }
        const siblings = this.stage.children

        if (siblings) {

            siblings.sort((a: Node, b: Node) => {
                return b.position.y - a.position.y
            })

            for (let i = 0; i < siblings.length; i++) {
                siblings[i].setSiblingIndex(i)
            }
        }

    }


    /** 生成舞台 */
    generateStage() {
        const bg = this.node.getChildByName('BG')
        const bgUITransform = bg.getComponent(UITransform)
        bgUITransform.width = G.StageSize.w
        bgUITransform.height = G.StageSize.h

        this.expStage = createUINode('expStage')
        this.expStage.setParent(bg)
        this.expStage.setSiblingIndex(2)
        this.expStage.setPosition(0, 0)

        this.stage = createUINode('stage')
        this.stage.setParent(bg)
        this.stage.setSiblingIndex(3)
        this.stage.setPosition(0, 0)
        const uiTransform = this.stage.getComponent(UITransform)
        uiTransform.setAnchorPoint(0.5, 0.5)
        uiTransform.width = G.StageSize.w
        uiTransform.height = G.StageSize.h
        this.graphics = this.stage.addComponent(Graphics)

        this.bulletStage = createUINode('bulletStage')
        this.bulletStage.setParent(bg)
        this.bulletStage.setSiblingIndex(4)
        this.bulletStage.setPosition(0, 0)

        this.labelStage = createUINode('labelStage')
        this.labelStage.setParent(bg)
        this.labelStage.setSiblingIndex(5)
        this.labelStage.setPosition(0, 0)

    }

    /** 通过状态数据初始化 */
    initByState(agentId: number) {

        for (const { position: { x, y }, agentId, type } of DataManager.Ins.state.enemies) {
            if (!DataManager.Ins.simulator.getAgentByAid(agentId)) {
                DataManager.Ins.simulator.addAgentById(new Vector2(x, y), agentId)
                DataManager.Ins.simulator.getAgentByAid(agentId).maxSpeed_ = GMoveSpeed[type] * 10
                GameLogic.Ins.setEnemyAgent(type)
            }
        }
        DataManager.Ins.simulator.setAgentId(agentId)
    }

    /** 申请断线重连 */
    applyOfflineLaunch() {
        NetworkManager.Ins.callApi(ApiMsgEnum.ApiApplyOfflineLaunch, {}).then((data) => {
            const { success, res, error } = data
            if (success) {
                this.init()
            } else {
                console.log(error)
            }
        })
    }

    /** 申请中途加入游戏 */
    applyMidJoinGame() {
        NetworkManager.Ins.callApi(ApiMsgEnum.ApiMidJoinGame, {}).then((data) => {
            const { success, res, error } = data
            this.init()
            if (!success) {
                showToast(error)
                console.log(error)
                return
            }
        })
    }

    /** 处理客户端输入 */
    handleClientInput(input: IClientInput) {
        const data = {
            frameId: DataManager.Ins.frameId++,
            input,
        }
        NetworkManager.Ins.send(ApiMsgEnum.MsgClientSync, data)

        // // 玩家移动，预测输入
        // if (input.type === InputTypeEnum.actorMove) {
        //     DataManager.Ins.applyInput(input)
        //     this.pendingMsg.push(data)
        // }
    }

    /** 同步游戏消息 */
    handleMsgServerSync({ inputs, state, agentId }: IMsgServerSync) {

        // // 回滚上一次服务器状态
        // DataManager.Ins.state = DataManager.Ins.lastState
        // 应用服务器状态
        for (const input of inputs) {
            DataManager.Ins.applyInput(input)
            // 保存逻辑帧状态
            DataManager.Ins.stateMap.set(DataManager.Ins.state.levelState.totalTime, deepClone(DataManager.Ins.state))
        }
        // // 记录最近服务器状态
        // DataManager.Ins.lastState = deepClone(DataManager.Ins.state)

        // // 过滤本地输入
        // this.pendingMsg = this.pendingMsg.filter(v => v.frameId > lastFrameId)
        // // 应用本地输入
        // for (const data of this.pendingMsg) {
        //     DataManager.Ins.applyInput(data.input)
        // }
        if (state) {
            DataManager.Ins.state.actors = deepClone(state.actors)
            DataManager.Ins.state.weapons = deepClone(state.weapons)
        }

        if (agentId) {
            this.initByState(agentId)
        }
    }

    /** 状态同步 */
    handleMsgStateSync({ state, agentId }: IMsgStateSync) {
        this.resetEntity()
        DataManager.Ins.state = deepClone(state)
        this.initEntity()
        this.initByState(agentId)
    }

    resetEntity() {
        DataManager.Ins.actorMap.forEach(v => {
            ObjectPoolManager.Ins.poolFree(v)
        })
        DataManager.Ins.enemyMap.forEach(v => {
            ObjectPoolManager.Ins.poolFree(v)
        })
        DataManager.Ins.weaponMap.forEach(v => {
            ObjectPoolManager.Ins.poolFree(v)
        })
        DataManager.Ins.bulletMap.forEach(v => {
            ObjectPoolManager.Ins.poolFree(v)
        })
        DataManager.Ins.enemyBulletMap.forEach(v => {
            ObjectPoolManager.Ins.poolFree(v)
        })
        DataManager.Ins.actorMap = new Map()
        DataManager.Ins.enemyMap = new Map()
        DataManager.Ins.weaponMap = new Map()
        DataManager.Ins.bulletMap = new Map()
        DataManager.Ins.enemyBulletMap = new Map()
        DataManager.Ins.stateMap = new Map()
        DataManager.Ins.behaviorTreeMap = new Map()
    }
    initEntity() {
        DataManager.Ins.state.enemies.forEach(enemy => {
            const { type, id } = enemy
            let tree = new Boss2Tree(enemy, DataManager.Ins.state)
            if (type === EntityTypeEnum.boss_1) {
                tree = new Boss1Tree(enemy, DataManager.Ins.state)
            }
            DataManager.Ins.behaviorTreeMap.set(id, new BehaviorManager(tree))
        });
    }
    onDestroy() {
        EventManager.Ins.off(EventTypeEnum.clientInput, this.handleClientInput, this)
        NetworkManager.Ins.unlistening(ApiMsgEnum.MsgServerSync, this.handleMsgServerSync, this)
        NetworkManager.Ins.unlistening(ApiMsgEnum.MsgStateSync, this.handleMsgStateSync, this)

    }
}